package proj

/*
 * 当心被批处理清理
 */

import (
	"dgo/goutils/utils"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

type xmlProperty struct {
	Name  string `xml:"name,attr"`
	Value string `xml:",chardata"`
}

type xmlFilter struct {
	Enabled  string        `xml:"enabled,attr"`
	Tag      string        `xml:"tag"`
	Level    string        `xml:"level"`
	LevelOp  string        `xml:"levelmatch"`
	Type     string        `xml:"type"`
	Property []xmlProperty `xml:"property"`
}

type xmlLoggerConfig struct {
	Filter []xmlFilter `xml:"filter"`
}

func SafeLoggerLoadXmlConfiguration(logger *utils.SafeLogger, filename string) error {

	fd, err := os.Open(filename)
	if err != nil {

		return errors.New(fmt.Sprintf("LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err))
	}

	contents, err := ioutil.ReadAll(fd)
	if err != nil {
		return errors.New(fmt.Sprintf("LoadConfiguration: Error: Could not read %q: %s\n", filename, err))
	}

	xc := new(xmlLoggerConfig)
	if err := xml.Unmarshal(contents, xc); err != nil {
		return errors.New(fmt.Sprintf("LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err))
	}

	var lvl utils.LogLevel
	for _, xmlfilt := range xc.Filter {

		if len(xmlfilt.Enabled) == 0 {
		} else {
			enabled := xmlfilt.Enabled != "false"
			if !enabled {
				continue
			}
		}

		lvlOp := utils.LogOp_GT

		switch xmlfilt.LevelOp {
		case "EQ":
			lvlOp = utils.LogOp_EQ
		case "LT":
			lvlOp = utils.LogOp_LT

		}

		switch xmlfilt.Level {
		case "FINEST":
			lvl = utils.LogLV_0
		case "FINE":
			lvl = utils.LogLV_1
		case "DEBUG":
			lvl = utils.LogLV_DEBUG
		case "TRACE":
			lvl = utils.LogLV_TRACE
		case "INFO":
			lvl = utils.LogLV_INFO
		case "WARNING":
			lvl = utils.LogLV_WARN
		case "ERROR":
			lvl = utils.LogLV_ERROR
		case "CRITICAL":
			lvl = utils.LogLV_ERROR
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
			lvl = utils.LogLV_INFO
		}

		switch xmlfilt.Type {
		case "console":
			filt, _ := xmlToConsoleLogWriter(filename, xmlfilt.Property, true)
			if filt != nil {
				logger.AddWriter(filt)
				filt.SetLvlFilter(lvl, lvlOp)
			}
		case "file":
			filt, _ := xmlToFileLogWriter(filename, xmlfilt.Property, true)
			if filt != nil {
				logger.AddWriter(filt)
				filt.SetLvlFilter(lvl, lvlOp)
			}
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
		}
	}

	return nil
}

func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (*utils.LoggerConsoleWriter, bool) {

	format := "[%d %t] [%L] %M (%s)\r\n"

	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		case "format":
			format = strings.Trim(prop.Value, " \r\n") + "\r\n"
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
		}
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	clw := utils.NewLoggerConsoleWriter()
	clw.SetMsgFormat(format)

	return clw, true
}

// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
func strToNumSuffix(str string, mult int) int {
	num := 1
	if len(str) > 1 {
		switch str[len(str)-1] {
		case 'G', 'g':
			num *= mult
			fallthrough
		case 'M', 'm':
			num *= mult
			fallthrough
		case 'K', 'k':
			num *= mult
			str = str[0 : len(str)-1]
		}
	}
	parsed, _ := strconv.Atoi(str)
	return parsed * num
}
func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*utils.LoggerTypeFileWriter, bool) {
	file := ""
	format := "[%d %t] [%L] %M (%s)\r\n"
	maxsize := 0
	cachesize := 0

	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		case "filename":
			file = strings.Trim(prop.Value, " \r\n")
		case "format":
			format = strings.Trim(prop.Value, " \r\n") + "\r\n"
		case "maxsize":
			maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
		case "cachesize":
			cachesize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
		}
	}

	// Check properties
	if len(file) == 0 {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
		return nil, false
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	flw := utils.NewLoggerTypeFileWriter(file)
	flw.SetMsgFormat(format)
	flw.ConfigMaxFileSize(int64(maxsize))
	flw.ConfigFileCacheSize(int64(cachesize))
	return flw, true
}
